﻿using System;
using System.Collections.Generic;
using System.IO;
using Cysharp.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;

namespace IQIGame.Onigao.GamePlay.Editors
{
    public class EffectModifyListener : AssetModificationProcessor
    {
        // private static string[] OnWillSaveAssets(string[] paths)
        // {
        //     for (int i = 0; i < paths.Length; i++)
        //     {
        //         var rPath = paths[i];
        //         if (IsEffectPrefab(rPath) && !modifyConfig.Contains(rPath))
        //         {
        //             modifyConfig.Add(rPath);
        //         }
        //     }
        //     Modify(modifyConfig).Forget();
        //     return null;
        // }

        static List<string> modifyConfig = new List<string>();

        const string pathCharacter = "Assets/Res/02_Character";
        const string pathMonster = "Assets/Res/03_Prefabs/Entity";
        const string pathBuff = "Assets/Res/14_Effect/Prefab/buff_effect";
        const string pathHit = "Assets/Res/14_Effect/Prefab/hit_effect";

        [MenuItem("GameEditor/Effect/批量处理战斗特效控制器")]
        public static void BatchEffect()
        {
            var rPlayerDir = Directory.GetDirectories(pathCharacter);
            RunDir(rPlayerDir);
            var rMonsterDir = Directory.GetDirectories(pathMonster);
            RunDir(rMonsterDir);

            Run(pathBuff);
            Run(pathHit);

            void RunDir(string[] dirs)
            {
                for (int i = 0; i < dirs.Length; i++)
                {
                    var rPath = dirs[i];
                    if (!Directory.Exists(rPath + "/Effect"))
                    {
                        continue;
                    }
                    var rEffectPath = rPath + "/Effect/";
                    Run(rEffectPath);
                }
            }

            void Run(string rEffectPath)
            {
                var rPrefabFile = Directory.GetFiles(rEffectPath, "*.prefab");
                for (int k = 0; k < rPrefabFile.Length; k++)
                {
                    var rPrefabPath = rPrefabFile[k];
                    var rPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(rPrefabPath);
                    if (SetEffectData(rPrefab))
                    {
                        EditorUtility.SetDirty(rPrefab);
                        PrefabUtility.SavePrefabAsset(rPrefab);
                    }
                }
            }

            AssetDatabase.Refresh();
        }

        private static async UniTaskVoid Modify(List<string> modify)
        {
            await UniTask.Yield();
            for (int i = 0; i < modify.Count; i++)
            {
                var rPath = modify[i];
                var rPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(rPath);
                if (SetEffectData(rPrefab))
                {
                    EditorUtility.SetDirty(rPrefab);
                    PrefabUtility.SavePrefabAsset(rPrefab);
                }
            }
            modify.Clear();
            AssetDatabase.Refresh();
        }

        private static bool IsEffectPrefab(string path)
        {
            return (((path.StartsWith(pathCharacter) || path.StartsWith(pathMonster)) &&
                     path.IndexOf("/Effect/") != -1) ||
                    path.StartsWith(pathBuff) ||
                    path.StartsWith(pathHit))
                   && path.EndsWith(".prefab");
        }

        private static bool SetEffectData(GameObject rPrefab)
        {
            bool bDirty = false;
            var rCtrl = rPrefab.GetComponent<EffectControlBehaviour>();
            if (rCtrl == null)
            {
                bDirty = true;
                rCtrl = rPrefab.AddComponent<EffectControlBehaviour>();
            }
            // 先看下自己有没有特效组件，没有就用第一个子节点的
            var rSelfParticle = rPrefab.GetComponent<ParticleSystem>();
            if (rSelfParticle)
            {
                bDirty = true;
                rCtrl.mainParticle = rSelfParticle;
            }
            else if (rPrefab.transform.childCount > 0)
            {
                var rMainParticle = rPrefab.transform.GetChild(0).GetComponent<ParticleSystem>();
                if (rCtrl.mainParticle != rMainParticle)
                {
                    bDirty = true;
                    rCtrl.mainParticle = rMainParticle;
                }
            }

            var fStartDuration = 0f;
            var fLoopDuration = 0f;
            var fEndDuration = 0f;
            var fMainDuration = 0f;
            var bLoop = false;
            bool bThreePart = false;
            if (rCtrl.controlData == null)
            {
                rCtrl.controlData = new List<EffectControlBehaviour.ParticleControlData>();
                bDirty = true;
            }
            List<ParticleSystem> rInCtrlParticle = new List<ParticleSystem>();
            for (int i = 0; i < rCtrl.controlData.Count; i++)
            {
                var rCtrlData = rCtrl.controlData[i];
                var fCurDur = 0f;
                var bCurLoop = false;
                for (int j = 0; j < rCtrlData.particles.Length; j++)
                {
                    var rParticle = rCtrlData.particles[j];
                    var main = rParticle.main;
                    bCurLoop = bCurLoop || main.loop;
                    fCurDur = Mathf.Max(fCurDur, main.duration + main.startDelay.constant);
                    rInCtrlParticle.Add(rParticle);
                }
                switch (rCtrlData.stageType)
                {
                    case EffectControlBehaviour.EEffectStageType.All:
                        fMainDuration = Mathf.Max(fMainDuration, fCurDur);
                        bLoop = bLoop || bCurLoop;
                        break;
                    case EffectControlBehaviour.EEffectStageType.Start:
                        fStartDuration = Mathf.Max(fStartDuration, fCurDur);
                        break;
                    case EffectControlBehaviour.EEffectStageType.Loop:
                        fLoopDuration = Mathf.Max(fLoopDuration, fCurDur);
                        bLoop = bLoop || bCurLoop;
                        break;
                    case EffectControlBehaviour.EEffectStageType.End:
                        fEndDuration = Mathf.Max(fEndDuration, fCurDur);
                        bThreePart = true;
                        break;
                }
            }

            var rComponents = rPrefab.GetComponentsInChildren<ParticleSystem>();
            foreach (var rParticle in rComponents)
            {
                if (rInCtrlParticle.Contains(rParticle))
                {
                    continue;
                }
                if (!bLoop && rParticle.main.loop)
                {
                    bLoop = true;
                }

                fMainDuration = Mathf.Max(fMainDuration, rParticle.main.duration + rParticle.main.startDelay.constant);
            }
            if (Math.Abs(rCtrl.allDuration - fMainDuration) > float.Epsilon)
            {
                bDirty = true;
                rCtrl.allDuration = fMainDuration;
            }
            if (Math.Abs(rCtrl.startDuration - fStartDuration) > float.Epsilon)
            {
                bDirty = true;
                rCtrl.startDuration = fStartDuration;
            }
            if (Math.Abs(rCtrl.loopDuration - fLoopDuration) > float.Epsilon)
            {
                bDirty = true;
                rCtrl.loopDuration = fLoopDuration;
            }
            if (Math.Abs(rCtrl.endDuration - fEndDuration) > float.Epsilon)
            {
                bDirty = true;
                rCtrl.endDuration = fEndDuration;
            }
            if (rCtrl.loop != bLoop)
            {
                bDirty = true;
                rCtrl.loop = bLoop;
            }
            if (rCtrl.threePart != bThreePart)
            {
                bDirty = true;
                rCtrl.threePart = bThreePart;
            }

            rCtrl.director = rPrefab.GetComponent<PlayableDirector>();
            if (rCtrl.director)
            {
                bDirty = true;
            }
            return bDirty;
        }
    }
}